﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using DLLx;
using RayDen.Library.Components.SystemComponents;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Scene;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Data;


namespace RayDen.RayEngine.IntersectionEngines
{
    using BaseRenderable = TriangleInfo;
    using Triangle = TriangleInfo;
    using RayInfo = RayData;
    using RenderableCollection = List<TriangleInfo>;

    [StructLayout(LayoutKind.Explicit)]
    public struct MPBvhData
    {
        [FieldOffset(0)]
        public BBox Bound;
        [FieldOffset(24)]
        public Int32 PrimOffset;
        [FieldOffset(24)]
        public Int32 SecondChildOffset;
        [FieldOffset(28)]
        public byte PrimitiveCount;
        [FieldOffset(29)]
        public byte Axis;
    }

    [StructLayout(LayoutKind.Explicit, Size = 32)]
    public struct GpuLBVHNode
    {
        [FieldOffset(0)]
        internal BBox bbox;
        [FieldOffset(24)]
        public UInt32 UData;

        [FieldOffset(28)]
        public byte axis;

        [FieldOffset(29)]
        public byte count;
        [FieldOffset(30)]
        public byte b1;
        [FieldOffset(31)]
        public byte b2;

        public UInt32 SkipIndex
        {
            get
            {
                return UData;
            }
            set
            {
                this.UData = value;
            }
        }

        /// <summary>
        /// !000 0000 bit is (0/1) whenever node is leaf or not
        /// </summary>
        public bool IsLeaf
        {
            get
            {
                return count > 0;
            }

        }


        public UInt32 PrimitiveIndex
        {
            get
            {
                return this.UData;
            }
            set
            {
                this.UData = value;
            }
        }

        /// <summary>
        /// 0000 00!! bits is node axis
        /// </summary>
        public byte Axis
        {
            get
            {
                return axis;
            }

            set
            {
                this.axis = value;
            }
        }
    }


    public unsafe class BvhDataAdapter
    {
        private RayEngineScene scene;
        private DLLx.BVHHandle bvhHandle;
        private DLLx.QBVHHandle qbvhHandle;


        public BvhDataAdapter(RayEngineScene scene)
        {
            this.scene = scene;
        }

        [StructLayout(LayoutKind.Explicit)]
        internal struct GpuBVHNodeU
        {
            [FieldOffset(0)]
            internal BoundingVolumeHierarchyAccelerationStructure.BBox bbox;
            [FieldOffset(24)]
            internal uint primitive;
            [FieldOffset(28)]
            internal uint skipIndex;

            [FieldOffset(0)]
            internal fixed float v0[0x8];

            internal GpuBVHNodeU(ref GpuSmitsBVHNode n)
            {
                this.bbox = new BoundingVolumeHierarchyAccelerationStructure.BBox(n.bbox.Min, n.bbox.Max);
                this.primitive = n.primitive;
                this.skipIndex = n.skipIndex;
            }
        }

        public GpuSmitsBVHNode[] BuildData()
        {
            var data = BuildHandle_CPP();
            
            return data;
            //return BuildHandle_CS();
        }

        private GpuSmitsBVHNode[] BuildHandle_CPP()
        {
            var triData = new DLLx.TriangleData(
                scene.Triangles.Select(tri => new DLLx.TriInfo() { id = 0, v1 = (int)tri.v0.VertexIndex, v2 = (int)tri.v1.VertexIndex, v3 = (int)tri.v2.VertexIndex }).ToArray(),
                scene.Vertices.Select(v => new DLLx.VertInfo(v.x, v.y, v.z)).ToArray());
            Tracer.TraceLine("Start building");
            using (bvhHandle = new BVHHandle(triData, 2u, 1, 60, 20, 1f))
            {
                var data = bvhHandle.GetData();
                Tracer.TraceLine("Complete!");
                return data.
                    //Except(new[]{data.First()}).
               Select(item => new GpuSmitsBVHNode()
               {
                   bbox = new AABBInfo(new Point(item.bbox.pMin.x, item.bbox.pMin.y, item.bbox.pMin.z), new Point(item.bbox.pMax.x, item.bbox.pMax.y, item.bbox.pMax.z)),
                   primitive = item.primitive,
                   skipIndex = item.skipIndex
               }).ToArray();
            }

        }

        public GpuSmitsBVHNode[] BuildTopData(AABB[] bounds)
        {
            using (
                BBoxData data =
                    new BBoxData(
                        bounds.Select(
                            item =>
                            new Box(new VertInfo(item.Min.x, item.Min.y, item.Min.z),
                                    new VertInfo(item.Max.x, item.Max.y, item.Max.z))).ToArray()))
            {
                using (TopBVHHandle handle = new TopBVHHandle(data, 2u, 4, 60, 20, 0.5f))
                {
                    var resdata = handle.GetData();
                    Tracer.TraceLine("Complete!");
                    return resdata.
                        //Except(new[]{data.First()}).
                   Select(item => new GpuSmitsBVHNode()
                   {
                       bbox = new AABBInfo(new Point(item.bbox.pMin.x, item.bbox.pMin.y, item.bbox.pMin.z), new Point(item.bbox.pMax.x, item.bbox.pMax.y, item.bbox.pMax.z)),
                       primitive = item.primitive,
                       skipIndex = item.skipIndex
                   }).ToArray();
                }
            }
        }

        public GpuSmitsBVHNode[] BuildData(TriangleMeshInfo mi)
        {
            var nrm = mi.StartVertice == 0 ? -1 : mi.StartVertice - 1;
            var triData = new DLLx.TriangleData(
                scene.Triangles.ToList().GetRange(mi.StartTriangle, mi.EndTriangle - mi.StartTriangle - 1).Select(tri => new DLLx.TriInfo()
                {
                    id = 0,
                    v1 = (int)tri.v0.VertexIndex - nrm,
                    v2 = (int)tri.v1.VertexIndex - nrm,
                    v3 = (int)tri.v2.VertexIndex - nrm
                }).ToArray(),
                scene.Vertices.ToList().GetRange(mi.StartVertice, mi.EndVertice - mi.StartVertice).Select(v => new DLLx.VertInfo(v.x, v.y, v.z)).ToArray());
            Tracer.TraceLine("Start building");
            using (bvhHandle = new BVHHandle(triData, 2u, 4, 60, 20, 0.5f))
            {
                var data = bvhHandle.GetData();
                Tracer.TraceLine("Complete!");
                return data.
                    //Except(new[]{data.First()}).
               Select(item => new GpuSmitsBVHNode()
               {
                   bbox = new AABBInfo(new Point(item.bbox.pMin.x, item.bbox.pMin.y, item.bbox.pMin.z), new Point(item.bbox.pMax.x, item.bbox.pMax.y, item.bbox.pMax.z)),
                   primitive = item.primitive,
                   skipIndex = item.skipIndex
               }).ToArray();
            }

        }

        public static GpuSmitsBVHNode[] CreateBVH(IEnumerable<TriangleDataInfo> triangles, IEnumerable<Point> vertices,
                                             uint treeType = 4u, int csamples = 4, int intersectionCost = 60,
                                             int traverseCost = 20, float emptyBonus = 20f)
        {
            var triData = new DLLx.TriangleData(
               triangles.Select(tri => new DLLx.TriInfo() { id = 0, v1 = (int)tri.v0.VertexIndex, v2 = (int)tri.v1.VertexIndex, v3 = (int)tri.v2.VertexIndex }).ToArray(),
               vertices.Select(v => new DLLx.VertInfo(v.x, v.y, v.z)).ToArray());
            Tracer.TraceLine("Start building");
            using (var bvhHandle = new BVHHandle(triData, treeType, csamples, intersectionCost, traverseCost, emptyBonus))
            {
                var data = bvhHandle.GetData();
                Tracer.TraceLine("Complete!");
                return data.
                    //Except(new[]{data.First()}).
               Select(item => new GpuSmitsBVHNode()
               {
                   bbox = new AABBInfo(new Point(item.bbox.pMin.x, item.bbox.pMin.y, item.bbox.pMin.z), new Point(item.bbox.pMax.x, item.bbox.pMax.y, item.bbox.pMax.z)),
                   primitive = item.primitive,
                   skipIndex = item.skipIndex
               }).ToArray();
            }
        }


        private GpuSmitsBVHNode[] BuildQHandle_CPP()
        {
            var triData = new DLLx.TriangleData(
                scene.Triangles.Select(tri => new DLLx.TriInfo() { id = 0, v1 = (int)tri.v0.VertexIndex, v2 = (int)tri.v1.VertexIndex, v3 = (int)tri.v2.VertexIndex }).ToArray(),
                scene.Vertices.Select(v => new DLLx.VertInfo(v.x, v.y, v.z)).ToArray());
            Tracer.TraceLine("Start building");
            using (qbvhHandle = new QBVHHandle(triData))
            {
                var data = qbvhHandle.GetData();
                Tracer.TraceLine("Complete!");
                return data.
                    //Except(new[]{data.First()}).
               Select(item => new GpuSmitsBVHNode()
               {
                   //bbox = new BoundingVolumeHierarchyAccelerationStructure.BBox(new Vector(item.bbox.pMin.x, item.bbox.pMin.y, item.bbox.pMin.z), new Vector(item.bbox.pMax.x, item.bbox.pMax.y, item.bbox.pMax.z)),
                   //primitive = item.primitive,
                   //skipIndex = item.skipIndex
               }).ToArray();
            }

        }
        private GpuSmitsBVHNode[] BuildHandle_CS()
        {
            var helper = new BoundingVolumeHierarchyAccelerationHelper();
            helper.Init(scene, scene.Meshes.ToArray());
            return
                helper.nodes.Select(
                    node =>
                    new GpuSmitsBVHNode()
                        {
                            bbox = new AABBInfo(node.Bounds),
                            primitive = node.PrimitiveIndex,
                            skipIndex = node.SkipIndex
                        }).ToArray();
        }


        public LNode[] BuildLData(out TriangleDataInfo[] sortedTris)
        {
            var helper =
                new
                    LinearBvhAccelleration();
            //BottomUpBvhAccelleration();
            helper.Init(scene, null);
            sortedTris = helper.triangles;
            return helper.GetData().ToArray();
        }
        public GpuLBVHNode[] BuildTDData(out TriangleDataInfo[] sortedTris)
        {
            var helper = new TopDownBvhAccell();
            helper.Init(scene, null);
            sortedTris = helper.triangles;

            return helper.GetData().ToArray();
        }

        public MPBvhData[] GetMpData()
        {
            var helper = new BVHBuildHelper();
            helper.Init(scene);
            return helper.GetData().ToArray();
        }

        public float[] BvhNodesAsFloatArray(GpuSmitsBVHNode[] nodes)
        {
            return
                nodes.SelectMany(n =>
                {

                    var lc = new GpuBVHNodeU(ref n);
                    var r = lc.v0;
                    var data = new[] { r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7] };
                    return data.ToArray();
                }).ToArray();
        }

    }



    public class BVHBuildHelper
    {
        private const int PrimitivesInNode = 4;
        private int costSamples = 4, isectCost = 80, traversalCost = 10;
        private float emptyBonus = 0.25f;

        public Node RootNode;

        #region Internal Types

        public class Node
        {
            public AABB Bound;
            public Node Left, Right;
            public Axis SplitAxis;
            public int StartOffset, nPrims;
        }


        internal class BVHBuildNode
        {
            public AABB bounds;
            public BVHBuildNode[] children;
            public int splitAxis, firstOffset, nPrims;

            internal bool IsLeaf
            {
                get { return children[0] != null && children[1] != null; }
            }
            internal BVHBuildNode()
            {
                children = new BVHBuildNode[2];
            }
            internal void InitLeaf(int first, int nP, ref AABB box)
            {
                bounds = box;
                nPrims = nP;
                firstOffset = first;
            }
            internal void InitInterior(BVHBuildNode left, BVHBuildNode right, int axis)
            {
                children[0] = left;
                children[1] = right;
                splitAxis = axis;
                bounds = left.bounds.Union(right.bounds);
                nPrims = 0;
            }
        }


        internal class LinearBVHNode
        {
            public AABB bbox;//+12+12+4
            public int primOffset;//+4
            public int secondChildOffset;//+4
            public byte nPrimitives;//+1
            public byte axis;//+1
            public void SetChildOffset(int sc)
            {
                this.secondChildOffset = sc;
            }
            public void SetOffset(int offset)
            {
                this.primOffset = offset;
            }
        }

        internal struct BBox
        {
            public Point Min;
            public Point Max;

            public BBox(AABB b)
            {
                this.Max = b.Max;
                this.Min = b.Min;
            }

            public static bool IntersectBox(BBox box, RayInfo ray)
            {
                float t0 = ray.Min;
                float t1 = ray.Max;

                var rayInvDir = 1f / ray.Dir;
                //1f /ray.Dir;

                float invDirX = rayInvDir.x;
                if (!process_box_coord(ref t0, ref t1, box.Min.x - ray.Org.x, box.Max.x - ray.Org.x, invDirX))
                    return false;

                float invDirY = rayInvDir.y;
                if (!process_box_coord(ref t0, ref t1, box.Min.y - ray.Org.y, box.Max.y - ray.Org.y, invDirY))
                    return false;

                float invDirZ = rayInvDir.z;
                if (!process_box_coord(ref t0, ref t1, box.Min.z - ray.Org.z, box.Max.z - ray.Org.z, invDirZ))
                    return false;
                return true;
            }
            private static bool process_box_coord(ref float t0, ref float t1, float mnRO, float mxRO, float invRayDir)
            {
                float tNear = (mnRO) * invRayDir;
                float tFar = (mxRO) * invRayDir;
                // Update parametric interval from slab intersection $t$s
                if (tNear > tFar)
                    swap(ref tNear, ref tFar);
                t0 = tNear > t0 ? tNear : t0;
                t1 = tFar < t1 ? tFar : t1;
                if (t0 > t1)
                    return false;
                return true;
            }
            private static void swap(ref float tNear, ref  float tFar)
            {
                float tmp = tNear;
                tNear = tFar;
                tFar = tmp;
            }
        }

        [StructLayout(LayoutKind.Explicit, Size = 32)]
        internal struct LBVHNode
        {
            [FieldOffset(0)]
            public BBox Bound;
            [FieldOffset(24)]
            public Int32 PrimOffset;
            [FieldOffset(24)]
            public Int32 SecondChildOffset;
            [FieldOffset(28)]
            public byte PrimitiveCount;
            [FieldOffset(29)]
            public byte Axis;
        }

        #endregion

        private Point[] sceneVertices;
        private Triangle[] triangles;
        private List<TriangleMeshInfo> meshes;
        private int bvhNodesCount;

        public void BuildTree(List<Triangle> primitives, int start, int end)
        {


            Tracer.TraceLine("Building accelleration {0}  {1} Node size in bytes", primitives.Count, Marshal.SizeOf(typeof(LBVHNode)));

            if (start - end == 0)
                return;
            AABB bound = primitives.GetRange(start, end - start).Select(prim => prim.WorldBound(sceneVertices)).Aggregate((b1, b2) => b1.Union(b2));


            RootNode = new Node() { Bound = bound, SplitAxis = (Axis)bound.MaximumExtent() };
            bvhNodesCount++;
            if ((end - start) <= PrimitivesInNode)
            {
                RootNode.StartOffset = start;
                RootNode.nPrims = end - start;
                return;
            }
            RootNode.Left = this.BuildSubTree(primitives, start, (start + end) / 2);
            RootNode.Right = this.BuildSubTree(primitives, (start + end) / 2, end);
        }

        private BVHBuildNode rootNode;

        public void BuildSAH()
        {

        }
        internal interface INodeComparer
        {
            bool Compare(Triangle p);
        }

        internal class MidComparer : INodeComparer
        {

            public int dim;
            public float pMid;
            public Point[] vertices;

            public bool Compare(Triangle p)
            {
                return p.Center(vertices)[dim] < pMid;
            }

        }
        private Node BuildSubTree(RenderableCollection primitives, int start, int end)
        {
            if (start - end == 0)
                return null;
            AABB bound = primitives.GetRange(start, end - start).Select(prim => prim.WorldBound(sceneVertices)).Aggregate((b1, b2) => b1.Union(b2));
            var node = new Node() { Bound = bound, SplitAxis = (Axis)bound.MaximumExtent() };
            bvhNodesCount++;
            if ((end - start) <= PrimitivesInNode)
            {
                node.StartOffset = start;
                node.nPrims = end - start;
                //node.Primitives = primitives.GetRange(start, (end - start));
                return node;
            }


            //float pmid;
            int axis = bound.MaximumExtent();
            Comparison<Triangle> tc = (t1, t2) => CompareTriangles(t1, t2, (Axis)bound.MaximumExtent());

            primitives.PartialSort(tc, start, end);

            //FindBestSplit(primitives, start, end, out pmid, out axis);



            //var pmid = .5f * (bound.Min[axis] + bound.Max[axis]);
            //FindBestSplit(primitives.GetRange(start, end-start), start, end, out pmid, out axis);
            //var cmp = new MidComparer() { dim = axis, pMid = pmid, vertices = sceneVertices};
            node.SplitAxis = (Axis)axis;

            var mid =
                //start+Algorithms.Partition(primitives.GetRange(start, end - start), cmp.Compare);
                (start + end) / 2;
            node.Left = this.BuildSubTree(primitives, start, mid);
            node.Right = this.BuildSubTree(primitives, mid, end);
            return node;
        }

        private List<LinearBVHNode> nodes;
        private LBVHNode[] bvhTree;
        private int FlattenBVHTree(Node node, ref int offset)
        {
            if (node == null)
            {
                return -1;
            }
            nodes[offset] = new LinearBVHNode() { bbox = node.Bound };
            var linearNode = nodes[offset];
            int myOffset = (offset)++;
            if (node.nPrims > 0)
            {
                linearNode.primOffset = node.StartOffset;
                linearNode.nPrimitives = (byte)node.nPrims;
            }
            else
            {
                linearNode.axis = (byte)node.SplitAxis;
                linearNode.nPrimitives = 0;
                FlattenBVHTree(node.Left, ref offset);
                linearNode.secondChildOffset = FlattenBVHTree(node.Right, ref offset);
            }
            return myOffset;
        }

        private void FlattenTree()
        {
            int offset = 0;
            nodes = Enumerable.Range(0, bvhNodesCount).Select(i => new LinearBVHNode()).ToList();
            this.FlattenBVHTree(RootNode, ref offset);
            bvhTree = nodes.Select(linearBvhNode => linearBvhNode.nPrimitives > 0
                                                        ? new LBVHNode()
                                                        {
                                                            Axis = linearBvhNode.axis,
                                                            Bound = new BBox(linearBvhNode.bbox),
                                                            PrimOffset = linearBvhNode.primOffset,
                                                            PrimitiveCount = linearBvhNode.nPrimitives
                                                        }
                                                        : new LBVHNode()
                                                        {
                                                            Axis = linearBvhNode.axis,
                                                            Bound = new BBox(linearBvhNode.bbox),
                                                            PrimitiveCount = linearBvhNode.nPrimitives,
                                                            SecondChildOffset = linearBvhNode.secondChildOffset
                                                        }).ToArray();
        }

        #region BVH Implementation

        public const int TraversalStackSize = 32;


        #endregion

        #region IAccellerationStructure Members

        public void Init(RayEngineScene scene)
        {
            var sceneModel = scene;
            this.sceneVertices = sceneModel.Vertices;
            AABB sceneBound = sceneVertices.Select(i => new AABB(i)).Aggregate((b1, b2) => b1.Union(b2));
            var ltriangles = scene.Triangles.Select(i => i.GetInfo()).ToList();
            Comparison<Triangle> tc = (t1, t2) => CompareTriangles(t1, t2,
                (Axis)sceneBound.MaximumExtent()
                //                Axis.PLANE_Z
                );
            //ltriangles.Sort(tc);


            Tracer.TraceLine("Building tree ");
            this.BuildTree(ltriangles, 0, ltriangles.Count);
            Tracer.TraceLine("BVH builded {0} nodes {1} object in leaf", bvhNodesCount, PrimitivesInNode);
            Tracer.TraceLine("Flattening tree...");

            this.triangles = ltriangles.ToArray();
            FlattenTree();
            Tracer.TraceLine("Complete");

        }

        public IEnumerable<MPBvhData> GetData()
        {
            foreach (var node in this.bvhTree)
            {
                {

                    yield return new MPBvhData()
                        {
                           Axis = node.Axis,
                           Bound = new IntersectionEngines.BBox() { Max = node.Bound.Max, Min = node.Bound .Min},
                           PrimOffset = node.PrimOffset,
                           PrimitiveCount = node.PrimitiveCount,
                           SecondChildOffset = node.SecondChildOffset
                        };
                }
            }
        }

        public int CompareTriangles(Triangle t1, Triangle t2, Axis a = Axis.PLANE_Z)
        {
            var p1 = t1.Center(sceneVertices);
            var p2 = t2.Center(sceneVertices);
            return p1.Component(a).CompareTo(p2.Component(a));
        }




        private void FindBestSplit(List<Triangle> list, int begin, int end, out float splitValue, out int bestAxis)
        {
            splitValue = 0;
            if (end - begin == 2)
            {
                splitValue = (list[begin].WorldBound(sceneVertices).Max[0] + list[begin].WorldBound(sceneVertices).Min[0] + list[end - 1].WorldBound(sceneVertices).Max[0] + list[end - 1]
                    .WorldBound(sceneVertices).Min[0]) / 2f;
                bestAxis = 0;
            }
            else
            {
                Point mean2 = new Point(), var = new Point();

                for (int i = begin; i < end; i++)
                    mean2 += list[i].WorldBound(sceneVertices).Max + list[i].WorldBound(sceneVertices).Min;
                mean2 /= end - begin;

                for (int i = begin; i < end; i++)
                {
                    Vector v = list[i].WorldBound(sceneVertices).Max + list[i].WorldBound(sceneVertices).Min - mean2;
                    v.x *= v.x;
                    v.y *= v.y;
                    v.z *= v.z;
                    var += v;
                }
                // Select axis with more variance
                if (var.x > var.y && var.x > var.z)
                    bestAxis = 0;
                else if (var.y > var.z)
                    bestAxis = 1;
                else
                    bestAxis = 2;

                if (costSamples > 1)
                {
                    AABB nodeBounds = new AABB();//!!!!
                    for (int i = begin; i < end; i++)
                        nodeBounds = nodeBounds.Union(list[i].WorldBound(sceneVertices));

                    Vector d = nodeBounds.Max - nodeBounds.Min;
                    float totalSA = (2f * (d.x * d.y + d.y * d.z + d.z * d.x));
                    float invTotalSA = 1f / totalSA;

                    // Sample cost for split at some points
                    float increment = 2f * d[bestAxis] / (costSamples + 1);
                    float bestCost = float.PositiveInfinity;
                    for (float splitVal = 2f * nodeBounds.Min[bestAxis] + increment; splitVal < 2f * nodeBounds.Max[bestAxis]; splitVal += increment)
                    {
                        int nBelow = 0, nAbove = 0;
                        AABB bbBelow = new AABB(Point.Zero), bbAbove = new AABB(Point.Zero);
                        for (int j = begin; j < end; j++)
                        {
                            if ((list[j].WorldBound(sceneVertices).Max[bestAxis] + list[j].WorldBound(sceneVertices).Min[bestAxis]) < splitVal)
                            {
                                nBelow++;
                                bbBelow = bbBelow.Union(list[j].WorldBound(sceneVertices));
                            }
                            else
                            {
                                nAbove++;
                                bbAbove = bbAbove.Union(list[j].WorldBound(sceneVertices));
                            }
                        }
                        Vector dBelow = bbBelow.Max - bbBelow.Min;
                        Vector dAbove = bbAbove.Max - bbAbove.Min;
                        float belowSA = 2f * ((dBelow.x * dBelow.y + dBelow.x * dBelow.z + dBelow.y * dBelow.z));
                        float aboveSA = 2f * ((dAbove.x * dAbove.y + dAbove.x * dAbove.z + dAbove.y * dAbove.z));
                        float pBelow = belowSA * invTotalSA;
                        float pAbove = aboveSA * invTotalSA;
                        float eb = (nAbove == 0 || nBelow == 0) ? emptyBonus : 0;
                        float cost = traversalCost + isectCost * (1f - eb) * (pBelow * nBelow + pAbove * nAbove);
                        // Update best split if this is lowest cost so far
                        if (cost < bestCost)
                        {
                            bestCost = cost;
                            splitValue = splitVal;
                        }
                    }
                }
                else
                {
                    // Split in half around the mean center
                    splitValue = mean2[bestAxis];
                }
            }
        }
        #endregion


        public static bool IntersectBox(AABB box, RayInfo ray)
        {
            float t0 = ray.minT;
            float t1 = ray.maxT;
            //float hitt0 = t0;
            //float hitt1 = t1;

            if (!process_box_coord(ref t0, ref t1, box.Min.x - ray.Org.x, box.Max.x - ray.Org.x, 1f / ray.Dir.x))
                return false;

            if (!process_box_coord(ref t0, ref t1, box.Min.y - ray.Org.y, box.Max.y - ray.Org.y, 1f / ray.Dir.y))
                return false;

            if (!process_box_coord(ref t0, ref t1, box.Min.z - ray.Org.z, box.Max.z - ray.Org.z, 1f / ray.Dir.z))
                return false;

            /*
            for (int i = 0; i < 3; ++i) {
                // Update interval for _i_th bounding box slab
                float invRayDir = 1f / ray.Direction[i];
                if (!process_box_coord(ref t0, ref t1, min[i] - ray.Origin[i], max[i] - ray.Origin[i], invRayDir)) return false;
            }
             * */
            //ray.MinT = t0;
            //ray.MaxT = t1;
            //hitt0 = t0;
            //hitt1 = t1;
            return true;
        }
        private static bool process_box_coord(ref float t0, ref float t1, float mnRO, float mxRO, float invRayDir)
        {
            float tNear = (mnRO) * invRayDir;
            float tFar = (mxRO) * invRayDir;
            // Update parametric interval from slab intersection $t$s
            if (tNear > tFar) swap(ref tNear, ref tFar);
            t0 = tNear > t0 ? tNear : t0;
            t1 = tFar < t1 ? tFar : t1;
            if (t0 > t1) return false;
            return true;
        }

        private static void swap(ref float tNear, ref  float tFar)
        {
            float tmp = tNear;
            tNear = tFar;
            tFar = tmp;
        }
    }
}
